﻿//    TestGenerator - experiment with piping data into ffmpeg in "pipefmt"
//    Copyright (C) 2012  Juergen Bohl

//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.

//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace TestGenerator
{
    #region using directives
    using System;
    using System.IO;
    using System.Text;
    using CommandLineParser;
    using CommandLineParser.Arguments;
    using CommandLineParser.Exceptions;
    #endregion

    internal enum OperationMode
    {
        Invalid,

        OutputPipeFormatFile,

        DriveFFMPEG
    }

    internal class Options
    {
        private OperationMode operationMode;

        private FileInfo outputFile;

        private int numberOfFrames;

        private bool withAudio;

        private int numberOfFramesInterval;

        private VideoFormat videoFormat;

        private AudioFormat audioFormat;

        private string ffmpegExecutable;

        public Options()
        {
            this.operationMode = OperationMode.Invalid;
        }

        public OperationMode OperationMode
        {
            get { return this.operationMode; }
            private set { this.operationMode = value; }
        }

        public string OutputFileName
        {
            get { return this.outputFile != null ? this.outputFile.FullName : null; }
        }

        public FileInfo OutputFile
        {
            get { return this.outputFile; }
            private set { this.outputFile = value; }
        }

        public int NumberOfFrames
        {
            get { return this.numberOfFrames; }
            private set { this.numberOfFrames = value; }
        }

        public bool WithAudio
        {
            get { return this.withAudio; }
            private set { this.withAudio = value; }
        }

        public int IntervalFramesCount
        {
            get { return this.numberOfFramesInterval; }
            private set { this.numberOfFramesInterval = value; }
        }

        public VideoFormat VideoFormat
        {
            get { return this.videoFormat; }
            private set { this.videoFormat = value; }
        }

        public AudioFormat AudioFormat
        {
            get { return this.audioFormat; }
            private set { this.audioFormat = value; }
        }

        public string FfmpegExecutable
        {
            get { return this.ffmpegExecutable; }
            private set { this.ffmpegExecutable = value; }
        }

        public void Init(string[] args)
        {
            var parser = new CommandLineParser();

            SwitchArgument driveThruFfmpeg = new SwitchArgument('d', "drive", "Set whether to drive the generated video through ffmpeg (or dump to a file).", false);

            FileArgument outputFile = new FileArgument('o', "output", "Output file")
                                          {
                                              FileMustExist = false,
                                              Optional = false
                                          };

            BoundedValueArgument<int> numberOfFrames = new BoundedValueArgument<int>('n', "framenumber", "The number of frames to generate.", 1, int.MaxValue);

            SwitchArgument withAudio = new SwitchArgument('a', "audio", "Generate an audio stream.", false);

            BoundedValueArgument<int> interval = new BoundedValueArgument<int>('i', "interval", "Number of frames with audio turned on.", 0, int.MaxValue);

            FileArgument ffmpegExe = new FileArgument('f', "ffmpeg", "ffmpeg executable to use.")
                                         {
                                             FileMustExist = false,
                                             Optional = true
                                         };

            parser.Arguments.Add(driveThruFfmpeg);
            parser.Arguments.Add(outputFile);
            parser.Arguments.Add(numberOfFrames);
            parser.Arguments.Add(withAudio);
            parser.Arguments.Add(interval);
            parser.Arguments.Add(ffmpegExe);
            try
            {
                /* parse the command line */
                parser.ParseCommandLine(args);
                /* show parsed values for debugging purposes */
                parser.ShowParsedArguments();

                if (driveThruFfmpeg.Parsed && driveThruFfmpeg.Value == true)
                {
                    this.OperationMode = OperationMode.DriveFFMPEG;
                }
                else
                {
                    this.OperationMode = OperationMode.OutputPipeFormatFile;
                }

                if (outputFile.Parsed)
                {
                    this.OutputFile = outputFile.Value;
                }

                this.NumberOfFrames = numberOfFrames.Parsed ? numberOfFrames.Value : 250;

                this.WithAudio = withAudio.Parsed && withAudio.Value;

                this.IntervalFramesCount = interval.Parsed ? interval.Value : 0;

                this.FfmpegExecutable = ffmpegExe.Parsed ? ffmpegExe.StringValue :null;

                this.InitializeAVFormats();
            }
            // CommandLineException is thrown when there is an error during parsing 
            catch (CommandLineException e)
            {
                Console.WriteLine(e.Message);
                /* you can help the user by printing all the possible arguments and their
                 * description, CommandLineParser class can do this for you.
                 */
                parser.ShowUsage();
            }
        }

        public void PrintSummary()
        {
            Console.Write(this.SummarizeOptions());
        }

        private void InitializeAVFormats()
        {
            this.VideoFormat = new VideoFormat(VideoSampleType.YV12, 720, 576,25,1);
            if (this.WithAudio)
            {
                this.AudioFormat = new AudioFormat(AudioSampleType.PCM_S16LE, 2, 48000);
            }
            else
            {
                this.AudioFormat = null;
            }
        }

        private string SummarizeOptions()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("Number of frames: {0}", this.NumberOfFrames);
            sb.AppendLine();
            sb.AppendFormat("Audio : {0}", this.WithAudio ? "yes" : "no");
            sb.AppendLine();
            return sb.ToString();
        }
    }
}
